#include "head.h"


/*
 * 功能：简化处理路径字符串，解析".",".."等特殊路径
 * 参数：
 *   originalPath - 输入的原始路径字符串
 *   curPath - 输出的简化后的路径(缓冲区需足够大)
 *   pdepth - 输入输出参数，当前路径深度
 *   slash - 记录路径中'/'位置的数组
 *   user - 用户信息结构体指针
 * 返回值：
 *   成功返回0，失败返回-1
 */
int simPath(char *originalPath, char *curPath, int *pdepth, int *slash, user_t *user) {
    int depth = *pdepth;  // 获取当前路径深度
    
    /* 原传入路径首字符为/代表绝对路径，从根目录开始 */
    if(originalPath[0] == '/') {
        bzero(curPath, 1024);          // 清空当前路径缓冲区
        bzero(slash, 100*sizeof(int)); // 清空斜杠位置数组
        strcpy(curPath, user->username);              // 转换为相对路径格式
        slash[0] = strlen(user->username);                  // 记录第一个斜杠位置
    }
    
    /* 使用strtok_r分割路径字符串 */
    char *saveptr;                     // strtok_r内部使用的指针
    char *p = strtok_r(originalPath, "/", &saveptr);  // 首次分割
    
    /* 遍历所有路径段 */
    while(p != NULL) {
        /* 处理上级目录".." */
        if(!strcmp(p, "..")) {
            if(depth == 0) {           // 已经在根目录，不能再向上
                RETERROR2("Invalid path.");
                return -1;
            } else {                  // 正常处理上级目录
                --depth;               // 深度减1
                curPath[slash[depth]] = 0;  // 截断路径字符串
            }
        }
        /* 忽略当前目录"." */
        else if(!strcmp(p, ".")) {
            continue;                  // 直接跳过处理
        }
        /* 处理普通目录/文件名 */
        else {
            int curlen = strlen(curPath);  // 当前路径长度
            curPath[curlen] = '/';        // 添加路径分隔符
            curPath[++curlen] = 0;       // 字符串结束符
            strcat(curPath, p);          // 拼接路径段
            curlen += strlen(p);          // 更新路径长度
            curPath[curlen] = 0;          // 确保字符串结束
            slash[++depth] = curlen;      // 记录新斜杠位置
        }
        p = strtok_r(NULL, "/", &saveptr); // 继续分割下一段
    }
    
    *pdepth = depth;  // 更新路径深度
    return 0;         // 成功返回
}

/*
 * 功能：接收客户端发送的命令数据包
 * 参数：
 *   user - 用户信息结构体指针
 *   train - 小火车
 * 返回值：
 *   成功返回0
 */
int recvOrderTrain(user_t *user, train_t *train) {
    // 接收命令类型(4字节)
    recv(user->sockfd, &train->type, sizeof(int), MSG_WAITALL);
    // 接收命令参数(3个bool值)
    recv(user->sockfd, train->args, 4*sizeof(bool), MSG_WAITALL);
    // 接收路径长度(4字节)
    recv(user->sockfd, &train->len, sizeof(int), MSG_WAITALL);
    // 接收路径数据(变长)
    recv(user->sockfd, train->path, train->len, MSG_WAITALL);
    return 0;
}

/*
 * 功能：接收并处理客户端发送的各种命令（核心逻辑）
 * 参数：
 *   user - 用户信息结构体指针
 * 返回值：
 *   成功返回0，失败返回-1
 */
int acceptOrder(user_t *user) {
    train_t train;  // 命令数据包
    
    // 1. 接收命令数据
    recvOrderTrain(user, &train);
    
    // 2. 初始化路径处理相关变量
    char destPath[1024] = {0};      // 目标路径缓冲区
    int slash[100] = {0};           // 斜杠位置数组
    int depth = user->depth;        // 当前路径深度
    strcpy(destPath, user->usercur); // 复制当前路径
    memcpy(slash, user->slash, (depth+1) * sizeof(int)); // 复制斜杠位置
    
    // 3. 简化处理路径
    int ret = simPath(train.path, destPath, &depth, slash, user);
    if(ret == -1) {
        return -1;
    }
    
    // 4. 根据命令类型分发处理
    switch(train.type) {
        case CD:    CDOrder(user, destPath, slash, depth); break;    // 切换目录
        case LS:    LSOrder(user, destPath, train.args); break;     // 列出目录
        case PUTS:  PUTSOrder(user, destPath); break;              // 上传文件
        case GETS:  GETSOrder(user, destPath); break;              // 下载文件
        case MKDIR: MKDIROrder(user, destPath); break;             // 创建目录
        case RMDIR: RMDIROrder(user, destPath, train.args); break; // 删除目录
        case REMOVE:REMOVEOrder(user, destPath); break;            // 删除文件
        case PWD:   PWDOrder(user); break;                         // 当前路径
        default: return -1;                                       // 无效命令
    }
    
    return 0;
}

/*
 * 功能：处理切换目录命令
 * 参数：
 *   user - 用户信息结构体指针
 *   newPath - 新路径字符串
 *   newSlash - 新路径斜杠位置数组
 *   newDepth - 新路径深度
 * 返回值：
 *   成功返回0，失败返回-1
 */
int CDOrder(user_t *user, char *newPath, int *newSlash, int newDepth) {
    // 1. 检查路径是否存在
    int ret = access(newPath, F_OK);
    if(ret == -1) {
        RETERROR1();  // 返回错误信息
        return -1;
    }
    
    // 2. 更新用户当前路径信息
    bzero(user->usercur, 1024);                // 清空当前路径
    bzero(user->slash, 100 * sizeof(int));     // 清空斜杠位置数组
    strcpy(user->usercur, newPath);            // 设置新路径
    memcpy(user->slash, newSlash, sizeof(int) * (newDepth+1)); // 复制斜杠位置
    user->depth = newDepth;                    // 更新路径深度
    
    RETOK("cd success!");  // 返回成功信息
    return 0;
}


/*
 * 功能：处理列出目录命令
 * 参数：
 *   user - 用户信息结构体指针
 *   destPath - 目标路径
 *   args - 命令参数数组
 * 返回值：
 *   成功返回0，失败返回-1
 */
int LSOrder(user_t *user, char *destPath, bool *args) {
    // 检查目录是否存在
    int ret = access(destPath, F_OK);
    if(ret == -1) {
        RETERROR1();
        return -1;
    }

    char retInfo[4096] = {0};  // 结果信息缓冲区
    DIR *dir = opendir(destPath);  // 打开目录
    struct dirent *entry;
    
    if(dir == NULL) {
        RETERROR1();
        return -1;
    }

    // 遍历目录项
    while((entry = readdir(dir)) != NULL) {
        // 根据参数决定是否显示隐藏文件
        if(args[0] || entry->d_name[0] != '.') {
            char buf1[1024] = {0};
            
            // 如果参数3为真，显示inode号
            if(args[2]){
                sprintf(buf1, "%lu ", entry->d_ino);
            }

            // 根据参数1决定显示格式
            if(!args[1]) {
                // 简单格式：文件名+制表符
                char buf2[1024] = {0};
                sprintf(buf2, "%s\t", entry->d_name);
                strcat(buf1, buf2);
                strcat(retInfo, buf1);
            } else {
                // 详细格式：调用ls_l函数
                char buf2[1024] = {0};
                ls_l(buf2, 1024, entry);
                strcat(buf1, buf2);
                strcat(retInfo, buf1);
            }
        }
    }
    
    closedir(dir);
    // 发送结果信息
    TLVResponse(user->sockfd, retInfo, IS_OK);
    return 0;
}

/*
 * 功能：处理文件上传命令
 * 参数：
 *   user - 用户信息结构体指针
 *   destPath - 目标路径
 * 返回值：
 *   成功返回0，失败返回-1
 */
int PUTSOrder(user_t *user, char *destPath) {
    // 打开文件，如果不存在则创建
    int fd = open(destPath, O_RDWR|O_CREAT|O_TRUNC, 0666);
    if(fd == -1) {
        RETERROR1();
        return -1;
    }

    // 获取文件当前大小
    int ret = lseek(fd, 0, SEEK_END);
    lseek(fd, 0, SEEK_SET);
    
    // 发送文件长度
    int type = FILE_LEN;
    char buf[16] = {0};
    sprintf(buf, "%d", ret);
    TLVResponse(user->sockfd, buf, type);

    // 接收文件传输指令
    train_t train;
    recv(user->sockfd, &train.type, sizeof(int), MSG_WAITALL);
    
    if(train.type == SEND_FILE) {
        // 接收文件长度并开始接收文件
        recv(user->sockfd, &train.len, sizeof(int), MSG_WAITALL);
        recvFile(user, fd, ret, train.len);
    } else if(train.type == CANCEL) {
        // 取消传输
        RETOK("Puts canceled.");
        close(fd);
    }
    
    return 0;
}

/*
 * 功能：接收文件内容
 * 参数：
 *   user - 用户信息结构体指针
 *   fd - 文件描述符
 *   offset - 文件偏移量
 *   fileSize - 文件大小
 * 返回值：
 *   成功返回0
 */
int recvFile(user_t *user, int fd, int offset, int fileSize) {
    // 调整文件大小并映射到内存
    ftruncate(fd, fileSize);
    char *p = (char*)mmap(NULL, fileSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    
    // 接收文件数据
    recv(user->sockfd, p+offset, fileSize-offset, MSG_WAITALL);
    
    RETOK("puts success.");
    close(fd);
    return 0;
}

/*
 * 功能：处理文件下载命令
 * 参数：
 *   user - 用户信息结构体指针
 *   destPath - 目标路径
 * 返回值：
 *   成功返回0，失败返回-1
 */
int GETSOrder(user_t *user, char *destPath) {
    // 打开文件
    int fd = open(destPath, O_RDONLY);
    if(fd == -1) {
        RETERROR1();
        return -1;
    }

    RETOK("Got the file.");
    
    // 接收偏移量参数
    train_t train;
    recvOrderTrain(user, &train);
    int offset = atoi(train.path);
    
    // 检查文件大小
    int fileSize = lseek(fd, 0, SEEK_END);
    if(fileSize < offset) {
        RETERROR2("Wrong file");
        close(fd);
        return -1;
    }
    
    // 发送文件
    sendFile(user, fd, offset, fileSize);
    return 0;
}

/*
 * 功能：发送文件内容
 * 参数：
 *   user - 用户信息结构体指针
 *   fd - 文件描述符
 *   offset - 文件偏移量
 *   fileSize - 文件大小
 * 返回值：
 *   成功返回0
 */
int sendFile(user_t *user, int fd, int offset, int fileSize) {
    // 调整文件大小并映射到内存
    ftruncate(fd, fileSize);
    char *p = (char *)mmap(NULL, fileSize, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
    
    // 发送文件数据
    send(user->sockfd, p+offset, fileSize-offset, MSG_NOSIGNAL);
    
    RETOK("Gets success.");
    close(fd);
    return 0;
}

/*
 * 功能：处理创建目录命令
 * 参数：
 *   user - 用户信息结构体指针
 *   destPath - 目标路径
 * 返回值：
 *   成功返回0，失败返回-1
 */
int MKDIROrder(user_t *user, char *destPath) {
    // 检查目录是否已存在
    int ret = access(destPath, F_OK);
    if(ret == 0) {
        DIR *dir = opendir(destPath);
        if(dir != NULL) {
            RETERROR2("Dir exist.");
            closedir(dir);
            return -1;
        }
    }
    
    // 创建目录
    ret = mkdir(destPath, 0777);
    if(ret == -1) {
        RETERROR1();
        return -1;
    }
    
    RETOK("mkdir success.");
    return 0;
}

/*
 * 功能：处理删除目录命令
 * 参数：
 *   user - 用户信息结构体指针
 *   destPath - 目标路径
 *   args - 命令参数数组
 * 返回值：
 *   成功返回0，失败返回-1
 */
int RMDIROrder(user_t *user, char *destPath, bool *args) {
    // 打开目录
    DIR *dir = opendir(destPath);
    if(dir == NULL) {
        RETERROR1();
        return -1;
    }

    // 根据参数决定删除方式
    if(!args[0]) {
        // 普通删除（目录必须为空）
        int ret = rmdir(destPath);
        if(ret == -1) {
            RETERROR1();
            return -1;
        }
        RETOK("rmdir success.");
    } else {
        // 递归删除
        closedir(dir);
        RMDIR_ROrder(user, destPath);
    }
    
    return 0;
}



/*
 * 功能：递归删除目录及其内容
 * 参数：
 *   user - 用户信息结构体指针
 *   destPath - 要删除的目录路径
 * 返回值：
 *   成功返回0，失败返回-1
 * 注意：
 *   1. 会跳过"."和".."目录
 *   2. 递归删除所有子目录和文件
 */
int RMDIR_ROrder(user_t *user, char *destPath) {
    // 打开目标目录
    DIR *dir = opendir(destPath);
    struct dirent *entry;
    
    // 遍历目录项
    while((entry = readdir(dir)) != NULL) {
        // 跳过当前目录和上级目录
        if(!strcmp(entry->d_name, "..") || !strcmp(entry->d_name, ".")) {
            continue;
        }
        
        // 构建完整路径
        char newPath[1024] = {0};
        sprintf(newPath, "%s/%s", destPath, entry->d_name);
        
        // 处理文件或目录
        if(entry->d_type != DT_DIR) {
            // 删除文件
            int ret = unlink(newPath);
            if(ret != 0) {
                RETERROR1();  // 返回错误信息
                return -1;
            }
        } else {
            // 递归删除子目录
            RMDIR_ROrder(user, newPath);
        }
    }
    
    // 删除空目录
    rmdir(destPath);
    RETOK("rmdir success.");  // 返回成功信息
    return 0;
}

/*
 * 功能：删除文件或目录
 * 参数：
 *   user - 用户信息结构体指针
 *   destPath - 要删除的路径
 * 返回值：
 *   成功返回0，失败返回-1
 * 注意：
 *   1. 先尝试作为文件删除
 *   2. 若失败再尝试作为目录删除
 */
int REMOVEOrder(user_t *user, char *destPath) {
    // 检查路径是否存在
    int ret = access(destPath, F_OK);
    if(ret == -1) {
        RETERROR1();
        return -1;
    }
    
    // 先尝试删除文件
    ret = unlink(destPath);
    if(ret == 0) {
        RETOK("remove success.");
    } else {
        // 文件删除失败，尝试删除目录
        ret = rmdir(destPath);
        if(ret == -1) {
            RETERROR1();
            return -1;
        } else {
            RETOK("Remove success.");
        }
    }
    
    return 0;
}

/*
 * 功能：获取当前工作目录
 * 参数：
 *   user - 用户信息结构体指针
 * 返回值：
 *   成功返回0
 * 注意：
 *   通过TLV格式返回当前目录路径
 */
int PWDOrder(user_t *user) {
    char buf[1024] = {0};
    // 获取当前工作目录
    getcwd(buf, sizeof(buf));
    // 发送目录信息
    TLVResponse(user->sockfd, buf, IS_OK);
    return 0;
}



